require "autoexportcommon"

TableType=
{
  TT_CLASS=1,
  TT_CREATOR=2,
  TT_FUNC=3,
  TT_TYPE=4,
  TT_MEMBER=5,
  TT_ACCESS=6,
  TT_MEMBERARR=7,
  TT_GLOBAL=8,
  TT_ENUM=9,
  TT_CONST=10,
  TT_BASE=11,
  TT_GLOBALARRAY=12,
  TT_LUAFUNC=13,
}

ClassAccess=
{
  CA_PUBLIC=1,
  CA_PRIVATE=2,
  CA_PROTECTED=3,
}

local function GetTypePatt()
	local function findtype(tbtype)
	  tbtype.type=TableType.TT_TYPE
	  return tbtype
	end
	local pttpdesc=(lpeg.C"const" + lpeg.C"volatile")^-2 * ptspaces
	local pttypevar=lpeg.P"::"^-1 * ptvar * (lpeg.P"::" * ptvar)^0
	local ptref=lpeg.C"&"^-1 * ptspaces
	local ptpoint=lpeg.C"*"^-1 * ptspaces
	local ptposttype=pttpdesc * ptpoint * pttpdesc * ptref
	local ptrawtype=lpeg.Ct(pttpdesc * lpeg.C(pttypevar) * ptspaces * ptposttype) / findtype
	local ptcomplextype=lpeg.P{
	  "vtype",
	  vtype=lpeg.V"vttp" + ptrawtype,
	  vttp=lpeg.Ct(
		  ptrawtype * lpeg.P("<") * ptspaces * lpeg.V"vtype" * ptspaces * (lpeg.P"," * ptspaces * lpeg.V"vtype" * ptspaces)^0 * lpeg.P(">") * ptspaces * (lpeg.C"::" * ptspaces * lpeg.C(ptvar) * ptspaces)^0 * ptposttype
		) / findtype,
	}
	return ptcomplextype
end

local pttype=GetTypePatt()
local ptoneparam=pttype * ptvar * ptspaces * pattpairs("[","]")^-1 * (lpeg.P"=" * ptspaces * (ptvar + ptnum) * ptspaces)^-1
local ptfuncparam=(ptoneparam * (lpeg.P"," * ptoneparam)^0)^-1

local function GetFuncPatt()
	local function findfuncret(ret)
		local tb={}
		tb.type=TableType.TT_FUNC
		tb.ret=ret
		return tb
	end
	local function findfuncname(tb,name)
		tb.name=name
		return tb
	end
	local function findfuncparam(tb,param)
	    if tb.param==nil then
	      tb.param={}
	    end
	    tb.param[#tb.param+1]=param
		return tb
	end
	local function postfindfunc(tb)
	  if tb.ret[1]=="int" and #tb.param==1 and tb.param[1][1]=="lua_State" and tb.param[1][2]=="*" then
	    tb.type=TableType.TT_LUAFUNC
	  end
	  return tb
	end
	local ptfuncpost=lpeg.P"(" * ptspaces * ptfuncparam * lpeg.P")" * ptspaces * lpeg.C"const"^-1 * ptspaces * 
	  (
	    (lpeg.P"=" * ptspaces * lpeg.P"0" * ptspaces)^-1 * lpeg.P";" + pattpairs("{","}") * lpeg.P";"^-1
	  ) * ptspaces
	return ((lpeg.P"virtual" + lpeg.P"inline") * ptspaces)^-2 * ptspaces *  
	lpeg.Cf(
		lpeg.Cf(
		(pttype / findfuncret) * lpeg.C(ptvar),
		findfuncname
		) * ptspaces * ptfuncpost,
		findfuncparam
	) / postfindfunc
end

local ptfunc=GetFuncPatt()

local function GetCreatorPatt()
	local function findcreatorname(name)
		local tb={}
		tb.type=TableType.TT_CREATOR
		tb.name=name
		return tb
	end
	local function findcreatorparam(tb,param)
		tb[#tb+1]=param
		return tb
	end

	local ptcreatoroneinit=ptvar * ptspaces * pattpairs("(",")") * ptspaces
	local ptcreatorinit=lpeg.P":" * ptspaces * ptcreatoroneinit * (lpeg.P"," * ptspaces * ptcreatoroneinit)^0
	local ptcreatorpost=lpeg.P"(" * ptspaces * ptfuncparam * lpeg.P")" * ptspaces * ptcreatorinit^-1 * (lpeg.P";" + pattpairs("{","}")^-1) * ptspaces
	return lpeg.P"explicit"^-1 * ptspaces * lpeg.P"inline"^-1 * ptspaces * 
	  lpeg.Cf(ptvar / findcreatorname * ptspaces * ptcreatorpost,findcreatorparam)
end

local ptcreator=GetCreatorPatt()
local ptdestructor=((lpeg.P"virtual" + lpeg.P"inline") * ptspaces)^-2 * ptspaces * 
  lpeg.P"~" * ptvar * ptspaces * lpeg.P"(" * ptspaces * lpeg.P")" * ptspaces * (lpeg.P";" + pattpairs("{","}")^-1) * ptspaces

local function GetMemberPatt()
	local function findmembername(tb,name)
		tb.type=TableType.TT_MEMBER
		tb.name=name
		return tb
	end
	
	local function findmemberarray(tb,name)
		tb.type=TableType.TT_MEMBERARR
		tb.name=name
		return tb
	end

	local ptmember=lpeg.P"mutable"^-1 * ptspaces * lpeg.Cf(
	  lpeg.Ct(pttype) * lpeg.C(ptvar),
	  findmembername
	) * ptspaces * lpeg.P";" * ptspaces
	
	local ptmemberarray=lpeg.Cf(
	  lpeg.Ct(pttype) * lpeg.C(ptvar),
	  findmemberarray
	) * ptspaces * pattpairs("[","]") * ptspaces * lpeg.P";" * ptspaces
	return ptmember + ptmemberarray
end

local ptclassmember=GetMemberPatt()

local ptneststruct=lpeg.P"typedef"^-1 * ptspaces * (lpeg.P"struct" + lpeg.P"union") * ptspaces * 
  (ptvar * ptspaces)^-1 * pattpairs("{","}") * ptspaces * 
  (ptvar * ptspaces * pattpairs("[","]")^-1)^-1 * lpeg.P";" * ptspaces

local function GetClassMainPatt()
	local function findclass(cap)
		local tb={}
		tb.type=TableType.TT_CLASS
		if cap=="class" then
			tb.curaccess=ClassAccess.CA_PRIVATE
		elseif cap=="struct" then
			tb.curaccess=ClassAccess.CA_PUBLIC
		end
		return tb
	end
	local function findclassname(tb,name)
		tb.name=name
		return tb
	end
	local function findclassbase(tb,...)
		if ...~=nil then
			local param={...}
			local base={}
			base.type=TableType.TT_BASE
			for _,v in ipairs(param) do
			if v~="public" and v~="private" and v~="protected" then
				base[#base+1]=v
			end
			end
			if #base>0 then
			tb[#tb+1]=base
			end
		end
		return tb
	end
	local function findclasscontent(tbclass,cap)
		if cap=="public" then
			local tb={}
			tb.type=TableType.TT_ACCESS
			tb.access=ClassAccess.CA_PUBLIC
			cap=tb
		elseif cap=="private" then
			local tb={}
			tb.type=TableType.TT_ACCESS
			tb.access=ClassAccess.CA_PRIVATE
			cap=tb
		elseif cap=="protected" then
			local tb={}
			tb.type=TableType.TT_ACCESS
			tb.access=ClassAccess.CA_PROTECTED
			cap=tb
		end
		tbclass[#tbclass+1]=cap
		return tbclass
	end
	local ptclasskey=lpeg.C(lpeg.P"public" + lpeg.P"private" + lpeg.P"protected") * ptspaces
	local ptclassonebase=ptclasskey^-1 * ptspaces * lpeg.C(ptvar) * ptspaces
	local ptclassbase=ptclassonebase * (lpeg.P"," * ptspaces * ptclassonebase)^0
	local ptclassbeg=(
		lpeg.Cf(  
		  (lpeg.P"class" + lpeg.P"struct") / findclass * ptspaces * lpeg.C(ptvar) * ptspaces,
		  findclassname
		) * 
		(
		  lpeg.P":" * ptspaces * ptclassbase
		)^-1
	) / findclassbase * ptspaces * lpeg.P"{" * ptspaces

    local ptclassignore=lpeg.P"static" + lpeg.P"friend" + ptvar
	local ptstatic=pattsection(ptclassignore,";") * ptspaces
	
	local ptclasssec=ptclasskey * lpeg.P":" * ptspaces
	local ptclassbody=(ptclasssec + ptdestructor + ptcreator + ptfunc + ptclassmember + ptneststruct + ptstatic)^0 * ptspaces
	return lpeg.Cf(ptclassbeg * ptclassbody,findclasscontent) * lpeg.P"}" * ptspaces
end

local ptclassmain=GetClassMainPatt()
local ptclass=ptclassmain * lpeg.P";" * ptspaces

local function findglobalname(tb,name)
  tb.type=TableType.TT_GLOBAL
  tb.name=name
  return tb
end
local ptglobal=lpeg.P"extern"^-1 * ptspaces * 
  lpeg.Cf(
    lpeg.Ct(pttype) * lpeg.C(ptvar),
    findglobalname
  ) * ptspaces * lpeg.P";" * ptspaces

local function findglobalarray(tb,name,numstr)
  tb.type=TableType.TT_GLOBALARRAY
  tb.name=name
  tb.numstr=numstr
  return tb
end
local ptglobalarray=lpeg.P"extern"^-1 * ptspaces * 
    ((lpeg.Ct(pttype) * lpeg.C(ptvar) * ptspaces * lpeg.P"[" * lpeg.C(ptvar + ptnum) * lpeg.P"]") / findglobalarray) * 
   ptspaces * lpeg.P";" * ptspaces

local ptglobalcfunc=lpeg.P"extern"^-1 * ptspaces * ptfunc

local function GetEnumPatt()
	local function findenumname(enum,name)
		local tb={}
		tb.type=TableType.TT_ENUM
		tb.name=name
		return tb
	end
	local function findenum(tb,enum)
		if type(tb)~="table" then
			tb={}
		end
		tb.type=TableType.TT_ENUM
		tb[#tb+1]=enum
		return tb
	end

	local ptoneenum=lpeg.C(ptvar) * ptspaces * (lpeg.P"=" * ptexprcharset)^-1 * ptspaces * lpeg.P"," * ptspaces
	local ptlastenum=lpeg.C(ptvar) * ptspaces * (lpeg.P"=" * ptexprcharset)^-1 * ptspaces * lpeg.P","^-1 * ptspaces
	return lpeg.Cf(
		lpeg.Cf(
		lpeg.C"enum" * ptspaces * 
		lpeg.C(ptvar)^-1 * ptspaces,
		findenumname
		) * lpeg.P"{" * ptspaces * ptoneenum^0 * ptlastenum^-1 * lpeg.P"}" * ptspaces * lpeg.P";" * ptspaces
		,findenum
	)
end

local ptenum=GetEnumPatt()

local function findconstv(tb)
  tb.type=TableType.TT_CONST
  return tb
end
local ptconstv=lpeg.Ct(lpeg.P"static"^-1 * ptspaces * lpeg.P"const" * ptspaces * pttype * lpeg.C(ptvar) * ptspaces * lpeg.P"=" * ptspaces * ptexprcharset * lpeg.P";" * ptspaces) / findconstv

local function GetTypedefPatt()
	local ptsimpletype=lpeg.P"*"^-1 * ptvar * lpeg.P"*"^-1
	local ptposttypedef=ptsimpletype * ptspaces * (lpeg.P"," * ptspaces * ptsimpletype * ptspaces)^0
	return lpeg.P"typedef" * ptspaces * (ptclassmain + pttype) * ptposttypedef * lpeg.P";" * ptspaces
end

local pttypedef=GetTypedefPatt()

local ptdeclare=(lpeg.P"class" + lpeg.P"struct") * ptspaces * ptvar * ptspaces * lpeg.P";" * ptspaces
local isfilefail=false
local function findfailstr(str)
  isfilefail=true
  print("match fail:",str:sub(1,80))
end
local ptalwaysfail=lpeg.P(1)^1 / findfailstr

local ptfile=lpeg.Ct(
    (ptclass + ptdeclare + ptglobalarray + ptglobal + ptglobalcfunc + ptenum + ptconstv + pttypedef + ptalwaysfail)^0
  )
ptfile=ptspaces * ptfile * -1

function PasreSyntax(str)
  local ret=lpeg.match(ptfile,str)
  if isfilefail then
    return
  end
  --print("syntax tree:",ret)
  return ret
end
